package com.example.zyl.common.query.aspect;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.example.zyl.api.entity.OperateLog;
import com.example.zyl.common.enums.CommonCode;
import com.example.zyl.common.enums.RedisQueueEnum;
import com.example.zyl.common.exception.LogException;
import com.example.zyl.common.model.ResultErrorMsg;
import com.example.zyl.common.query.annotation.ApiLog;
import com.example.zyl.common.query.enums.BusinessType;
import com.example.zyl.common.utils.RedisDelayedQueueUtils;
import com.example.zyl.common.utils.ServletUtils;
import com.example.zyl.common.utils.ToolUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
@Order(2)
public class LogAspect {

    private final RedisDelayedQueueUtils queueUtils;

    @Around(value = "@annotation(com.example.zyl.common.query.annotation.ApiLog)")
    public Object logBefore(ProceedingJoinPoint pjp) {
        //开始计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        OperateLog operateLog = new OperateLog();


        Object result = null;

        try {
            //获取请求信息
            this.buildRequestLog(operateLog, pjp);

            //获取注解信息
            this.buildAnnotationLog(operateLog, pjp);

            //执行方法
            result = pjp.proceed();
        } catch (Throwable e) {
            operateLog.setStatus(CommonCode.SYSTEM_ERROR.getCode());
            //记录错误信息
            ResultErrorMsg errorMsg = new ResultErrorMsg();
            errorMsg.setMessage(e.getMessage());
            errorMsg.setException(e.getMessage());
            //转为json字符串
            String errorJson = JSON.toJSONString(errorMsg);
            operateLog.setErrorMsg(errorJson);
            log.error("记录日志失败 ===> [{}]", e.getMessage(), e);
            throw new LogException(e.getMessage());
        } finally {
            stopWatch.stop();
            //计算耗时
            operateLog.setCostTime(stopWatch.getTime());
            operateLog.setJsonResult(JSON.toJSONString(result));
            //记录日志
            queueUtils.addQueue(operateLog, RedisQueueEnum.OPERATE_LOG);
        }

        return result;
    }


    /**
     * 获取请求参数
     *
     * @param operateLog log
     */
    public void buildRequestLog(OperateLog operateLog,
                                ProceedingJoinPoint pjp) {
        //获取请求对象
        HttpServletRequest request = ServletUtils.getRequest();

        if (request == null) return;

        //获取请求参数
        Object[] args = pjp.getArgs();
        //参数转字符串
        String params = ToolUtils.argsArrayToString(args);
        //请求地址
        String url = request.getRequestURI();
        //请求方式
        String requestMethod = request.getMethod();
        //请求ip
        String ip = ServletUtil.getClientIP(request);
        //封装日志
        operateLog.setOperateId(IdWorker.getId());
        operateLog.setOperateUrl(url);
        operateLog.setRequestMethod(requestMethod);
        operateLog.setOperateIp(ip);
        operateLog.setOperateParam(params);
        operateLog.setOperateTime(LocalDateTime.now());
        operateLog.setStatus(CommonCode.SUCCESS.getCode());
    }


    public void buildAnnotationLog(OperateLog operateLog,
                                   ProceedingJoinPoint pjp) {

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        //获取注解信息
        ApiLog apiLog = signature.getMethod().getAnnotation(ApiLog.class);
        //获取业务类型
        BusinessType businessType = apiLog.businessType();
        //获取操作模块
        String title = apiLog.module();
        //请求方法路径
        String methodName = signature.getDeclaringType().getName();
        //封装日志
        operateLog.setTitle(title);
        //调用方法
        operateLog.setMethod(methodName);
        //业务类型
        operateLog.setBusinessType(businessType.getCode());
        //操作人
        if (StpUtil.isLogin()) {
            operateLog.setOperateName(StpUtil.getLoginId().toString());
        }
    }

}
